package com.liliudong.template.biz.service.sys.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liliudong.template.biz.mapper.sys.SysUserMapper;
import com.liliudong.template.biz.service.sys.SysMenuService;
import com.liliudong.template.biz.service.sys.SysRoleService;
import com.liliudong.template.biz.service.sys.SysUserRoleService;
import com.liliudong.template.biz.service.sys.SysUserService;
import com.liliudong.template.common.constant.ExMessageEnum;
import com.liliudong.template.common.constant.SaSessionConst;
import com.liliudong.template.common.constant.SecurityConst;
import com.liliudong.template.common.constant.SysRoleDataScopeEnum;
import com.liliudong.template.common.convert.sys.SysUserConvert;
import com.liliudong.template.common.dto.sys.SysUserAddOrUpdateDTO;
import com.liliudong.template.common.entity.sys.SysMenu;
import com.liliudong.template.common.entity.sys.SysRole;
import com.liliudong.template.common.entity.sys.SysUser;
import com.liliudong.template.common.exception.BizException;
import com.liliudong.template.common.security.LoginUser;
import com.liliudong.template.common.vo.PageVO;
import com.liliudong.template.common.vo.sys.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * sys用户服务impl
 *
 * @author ldli5
 * @date 2024/06/18
 */
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

	private final SysRoleService sysRoleService;

	private final SysMenuService sysMenuService;

	private final SysUserRoleService sysUserRoleService;

	@Override
	public SysUserLoginSuccessVO login(String username, String password) {
		SysUser oneByUsername = this.getOneByUsernameIgnoreDataPermission(username);
		if (oneByUsername == null) {
			throw new BizException(ExMessageEnum.LOGIN_FAIL);
		}
		if (!DigestUtil.bcryptCheck(password, oneByUsername.getPassword())) {
			throw new BizException(ExMessageEnum.LOGIN_FAIL);
		}
		StpUtil.login(oneByUsername.getId());
		// 缓存登录账户信息
		List<SysRole> sysRoles = sysRoleService.listByUserIdIgnoreDataPermission(oneByUsername.getId());
		Set<String> rolePermissions = sysRoles.stream().map(SysRole::getPermission).collect(Collectors.toSet());
		Set<String> permissions;
		Set<SysRoleDataScopeEnum> dataScopes;
		if (rolePermissions.contains(SecurityConst.SUPER_ADMIN_ROLE_PERMISSION)) {
			// 超管
			permissions = Set.of(SecurityConst.SUPER_ADMIN_ROLE_PERMISSION);
			dataScopes = Set.of(SysRoleDataScopeEnum.ALL);
			rolePermissions = Set.of(SecurityConst.SUPER_ADMIN_ROLE_PERMISSION);
		} else {
			Set<Long> roleIds = sysRoles.stream().map(SysRole::getId).collect(Collectors.toSet());
			permissions = sysMenuService.listPermissionByRoleIdsIgnoreDataPermission(roleIds);
			dataScopes = sysRoleService.listDataScopeByUserIdIgnoreDataPermission(oneByUsername.getId());
		}
		cacheSaSession(oneByUsername, dataScopes, permissions, rolePermissions);
		SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
		long expiresTime = System.currentTimeMillis() + tokenInfo.getTokenTimeout();
		return new SysUserLoginSuccessVO(tokenInfo.getTokenName(), tokenInfo.getTokenValue(), expiresTime);
	}

	@Override
	public SysUser getOneByUsernameIgnoreDataPermission(String username) {
		return baseMapper.selectByUsernameIgnoreDataPermission(username);
	}

	@Override
	public SysUserProfileVO profile() {
		SysUserProfileVO sysUserProfileVO = new SysUserProfileVO();

		long userId = StpUtil.getLoginIdAsLong();
		// 获取用户信息
		SysUser sysUser = this.getByIdIgnoreDataPermission(userId);
		sysUserProfileVO.setUsername(sysUser.getUsername());
		// 获取用户角色信息
		List<SysRole> sysRoles = sysRoleService.listByUserIdIgnoreDataPermission(userId);
		Set<String> rolePermissions = Set.of();
		Set<String> permissions = Set.of();
		Set<SysRoleDataScopeEnum> dataScopes = Set.of();
		List<SysMenu> sysMenus = List.of();

		if (CollUtil.isNotEmpty(sysRoles)) {
			List<Long> roleIds = sysRoles.stream().map(SysRole::getId).toList();
			rolePermissions = sysRoles.stream().map(SysRole::getPermission).collect(Collectors.toSet());
			dataScopes = sysRoles.stream().map(SysRole::getDataScope).collect(Collectors.toSet());
			if (rolePermissions.contains(SecurityConst.SUPER_ADMIN_ROLE_PERMISSION)) {
				// 超级管理员权限
				sysMenus = sysMenuService.listIgnoreDataPermission();
				rolePermissions = Set.of(SecurityConst.SUPER_ADMIN_ROLE_PERMISSION);
				dataScopes = Set.of(SysRoleDataScopeEnum.ALL);
				permissions = Set.of(SecurityConst.SUPER_ADMIN_ROLE_PERMISSION);
			} else {
				// 获取角色菜单信息
				sysMenus = sysMenuService.listVisibleByRoleIdsIgnoreDataPermission(roleIds);
				permissions = sysMenus.stream().map(SysMenu::getPermission).collect(Collectors.toSet());
			}
		}
		// 刷新缓存登录账户信息
		cacheSaSession(sysUser, dataScopes, permissions, rolePermissions);

		List<Tree<Long>> trees = sysMenuService.menus2Tree(sysMenus);

		sysUserProfileVO.setRolePermissions(rolePermissions);
		sysUserProfileVO.setPermissions(permissions);
		sysUserProfileVO.setMenuTree(trees);
		sysUserProfileVO.setId(userId);
		return sysUserProfileVO;
	}

	@Override
	public SysUser getByIdIgnoreDataPermission(long userId) {
		return baseMapper.selectByIdIgnoreDataPermission(userId);
	}

	@Override
	@Transactional
	public void addOne(SysUserAddOrUpdateDTO sysUserAddOrUpdateDTO) {
		Set<Long> roleIds = sysUserAddOrUpdateDTO.getRoleIds();
		SysUser sysUser = SysUserConvert.INSTANCE.dto2Entity(sysUserAddOrUpdateDTO);
		// 生成用户密码
		String password = DigestUtil.bcrypt(SecurityConst.DEFAULT_PASSWORD);
		sysUser.setPassword(password);
		// 保存用户
		this.save(sysUser);
		// 保存用户和角色关系
		sysUserRoleService.saveBatchWithUserId(sysUser.getId(), roleIds);
	}

	@Override
	@Transactional
	public void modifyById(Long userId, SysUserAddOrUpdateDTO sysUserAddOrUpdateDTO) {
		SysUser sysUser = this.getById(userId);
		if (Objects.isNull(sysUser)) {
			throw new BizException(ExMessageEnum.USER_NOT_EXIST);
		}
		SysUser sysUserUpdated = SysUserConvert.INSTANCE.dto2Entity(sysUserAddOrUpdateDTO);
		sysUserUpdated.setId(sysUser.getId());
		// 更新用户信息
		this.updateById(sysUserUpdated);
		// 更新角色信息
		sysUserRoleService.updateBatchWithUserId(sysUser.getId(), sysUserAddOrUpdateDTO.getRoleIds());
	}

	@Override
	public PageVO<SysUserPageVO> queryPage(Long current, Long size) {
		Page<SysUserPageVO> sysUserPage = baseMapper.selectExpandPage(Page.of(current, size));
		return PageVO.of(sysUserPage);
	}

	@Override
	public SysUserBaseVO detailById(Long id) {
		SysUser sysUser = this.getById(id);
		SysUserDetailVO sysUserDetailVO = SysUserConvert.INSTANCE.entity2DetailVo(sysUser);
		// 获取角色ID信息
		List<Long> roleIds = sysUserRoleService.listIdsByUserId(id);
		sysUserDetailVO.setRoleIds(roleIds);
		return sysUserDetailVO;
	}

	/**
	 * 缓存sa会话
	 *
	 * @param sysUser         sys用户
	 * @param dataScopes      数据作用域
	 * @param permissions     权限
	 * @param rolePermissions 角色权限
	 */
	private static void cacheSaSession(SysUser sysUser, Set<SysRoleDataScopeEnum> dataScopes, Set<String> permissions, Set<String> rolePermissions) {
		LoginUser loginUser = new LoginUser(sysUser.getId(), sysUser.getUsername(), sysUser.getDeptNo(), sysUser.getCreatedBy(), sysUser.getCreatedFrom());
		StpUtil.getTokenSession()
				.set(SaSessionConst.DATA_SCOPES, dataScopes)
				.set(SaSessionConst.DEPT_NO, sysUser.getDeptNo())
				.set(SaSession.USER, loginUser)
				.set(SaSession.PERMISSION_LIST, permissions)
				.set(SaSession.ROLE_LIST, rolePermissions);
	}

}
